<?php

namespace app\zyhsapi\controller;

use app\common\lib\Aes;
use app\common\lib\exception\ApiException;
use app\common\lib\IAuth;
use app\common\lib\Util;
use app\zyhsapi\model\ZyhsRecode;
use app\zyhsapi\model\ZyhsUser;
use think\Controller;
use think\Env;
use think\Log;

class ZyhsAuth extends Controller
{
    /**
     * headers信息
     * @var array
     */
    public $headers = [];

    public $uri = '';

    public $isLogin = false;
    /**
     * 登录用户的基本信息
     * @var array
     */
    public $user = null;

    /**
     * @var true
     */
    protected $simulationLogin = false;

    /**
     * 初始化的方法
     * @throws ApiException
     */
    protected function _initialize()
    {
        $this->checkRequestAuth();

        if (empty($this->uri))
        {
            $model = $this->request->module();
            $controller = $this->request->controller();
            $startNum = strpos($controller, '.');
            if ($startNum !== false)
            {
                $controller = substr($controller, $startNum + 1);
            }
            $action = $this->request->action(); // 全小写

            $this->uri = strtolower($model . '.' . $controller . '.' . $action);
        }

        // 获取用户登录信息
        $this->getLoginUserInfo();

        if (!$this->isLogin && $this->isMastLogin())
        {   // 未登录 判断接口是否需要登录  返回真则代表需要登录
            throw new ApiException('前往登录', 200, 405);
        }

        // 判断是否需要绑定手机号 true=>需要 false=>不需要
        if ($this->isLogin && !$this->user['tel'] && $this->isMustBindTel())
        {   // 已登陆 当前账号未绑定手机号  判定是否需要绑定手机   返回真 则代表需要
            throw new ApiException('根据网络安全法要求，请先绑定手机号', 200, 407);
        }

        if ($this->isLogin && $this->hasRecordCheck())
        {   // 已登录  判定是否需要档案   返回真 则代表需要 且 当前账号没有档案
            throw new ApiException('用户档案缺失', 200, 406);
        }
    }

    /**
     * 是否具有档案检查
     * @throws \Exception
     */
    public function hasRecordCheck()
    {
        if (isset($this->user['rid']) && $this->user['rid'])
        {   // 判断用户是否有自己的档案 有则返回 false
            return false;
        }
        // 没有自己的档案
        $rid = input('rid/d', 0);
        $rid1 = input('rid1/d', 0);
        if ($rid || $rid1)
        { // 请求参数含有 档案ID
            $rid = $rid ?: $rid1;
            $recodeInfo = (new ZyhsRecode())->getInfoById($rid, ['uid']);
            if ($recodeInfo['uid'] == $this->user['id'])
            {
                return false;
            }
        }
        //添加 档案验证接口数据
        $needRecordData = [

        ];

        if (in_array($this->uri, $needRecordData, true))
        {
            return true;
        }

        return false;
    }

    /**
     * 判断是否必须绑定手机号
     * @return bool true=>需要  false=>不需要
     */
    public function isMustBindTel()
    {
        $neednotTelData = [ // 免绑手机号接口
            'zyhsapi.app.update',  // APP 更新
            'zyhsapi.info.bindtel',  // 绑定手机号
            'zyhsapi.login.mobile', // 手机号登录
            'zyhsapi.login.teltokenlogin', // 手机号 快捷登录
            'zyhsapi.login.getcode', // 获取验证码

            'zyhsapi.login.phoneoneclick', // 手机号一键登录
            'zyhsapi.info.phoneoneclickbind',   // 手机号一键绑定手机号
        ];
        if (in_array($this->uri, $neednotTelData, true))
        {
            return false;
        }

        return true;
    }

    /**
     * 接口是否必须登录
     * @return bool
     */
    public function isMastLogin()
    {
        $neednotLoginData = [ // 免绑登录接口
            'zyhsapi.app.update',  // APP 更新
            'zyhsapi.index.index',  // APP 首页
            'zyhsapi.login.outerlogin',  // 三方登录
            'zyhsapi.login.getcode', // 获取验证码
            'zyhsapi.login.mobile', // 手机号登录
            'zyhsapi.system.about', // 关于我们
            'zyhsapi.info.usercenter', // 个人中心
            'zyhsapi.login.teltokenlogin', // 手机号 快捷登录
            'zyhsapi.consultchat.getconsultastrlist', // 专区咨询师列表
            'zyhsapi.consultinfo.asklist', // 大家都问
            'zyhsapi.consultchat.homepage', // 咨询师主页
            'zyhsapi.consultchat.commentlist', // 咨询师 评价列表
            'zyhsapi.consultinfo.zoneinfo', // 咨询专区
            'zyhsapi.consultchat.astrfollowlist', // 咨询师关注列表
            'zyhsapi.consultinfo.getcommenttag', // 评价标签列表
            'zyhsapi.consultinfo.rtcchatlist', // 我的咨询列表
            'zyhsapi.consultinfo.feedbackrtcchat', // 反馈
            'zyhsapi.questionanswer.index', // 问答首页
            'zyhsapi.questionanswer.questiondetail', // 问答详情

            'zyhsapi.complain.getreporttag', // 获取举报原因
            'zyhsapi.complain.report', // 举报

            'zyhsapi.login.phoneoneclick', // 手机号一键登录
            'zyhsapi.consultchat.getconsultchatidlist', // 获取 咨询对话记录 列表
            'zyhsapi.divination.getrecommendquestion', // 获取塔罗|骰子推荐问题

            'zyhsapi.goodrtclive.getliveroominfo', // 优质连线直播间 获取房间信息
            'zyhsapi.goodrtclive.getliveplaymediainfo', // 优质连线直播间 获取直播播放音频资源信息
            'zyhsapi.goodrtclive.getplaytoken', // 优质连线直播间 获取 播放token

            'zyhsapi.pxyz.index',   //  平行宇宙 首页
            'zyhsapi.pxyz.getcontentdetail',    // 平行宇宙 内容详情接口
            'zyhsapi.contentcomment.getcommentlist',    //  评论列表
            'zyhsapi.contentcomment.getcommentreplylist', // 评论回复 列表
        ];
        if (in_array($this->uri, $neednotLoginData, true))
        {
            return false;
        }

        return true;
    }

    /**
     * 判定是否登录
     * @throws ApiException
     */
    public function getLoginUserInfo()
    {
        if (!isset($this->headers['access-token']) || $this->headers['access-token'] == '') return;

        // access-token AES解密
        $accessUserToken = (new Aes())->decryptWithOpenssl($this->headers['access-token'], 'zyhsapp');

        if (empty($accessUserToken)) return;

        if (!preg_match('/||/', $accessUserToken))
        {
            throw new ApiException('您的账户登录过期，请重新登录。', 200, config('code.denied'));
        }

        [$token, $id] = explode('||', $accessUserToken);

        $id = (int)($id);
        // 账号模拟
        $simulationFrom = (int)Env::get('simulation_zhunle_good.from');
        $simulationTo = (int)Env::get('simulation_zhunle_good.to');
        // 不进行模拟
        if ($id !== $simulationFrom)
        {
            $map['id'] = $id;
            $map['token'] = $token;
        } else
        {
            // 如果登录的账号是用来模拟的那个账号，那么，修改为需要模拟的那个用户ID
            $map['id'] = $simulationTo ?: $id;
            if ($map['id'] === $simulationTo)
            {   // 模拟登陆
                $this->simulationLogin = true;
            }
        }

        $userinfo = (new ZyhsUser())->getInfoById($map['id']);

        if (empty($userinfo) || (!$this->simulationLogin && isset($map['token']) && $userinfo['token'] !== $map['token']))
        {
            throw new ApiException('当前账号登录过期，请您重新登录。', 200, config('code.denied'));
        }

        if ($userinfo['forbidden'] == 1)
        {
            throw new ApiException('您的账户登录过期，请重新登录。', 200, config('code.denied'));
        }
        // 判定时间是否过期
        if (isset($map['token']) && time() > $userinfo['token_expire'])
        {
            throw new ApiException('您的账户登录过期，请重新登录。', 200, config('code.denied'));
        }
        $this->user = $userinfo;
        /**
         * 定义全局常量 UID 方便日志打印带上用户ID信息
         */
        if (!defined('USERINFO_UID'))
        {
            define('USERINFO_UID', $userinfo['id']);
        }
        // 记录登录状态
        $this->isLogin = true;
    }

    /**
     * 检查每次app请求的数据是否合法
     * @throws ApiException
     */
    public function checkRequestAuth()
    {
        // 首先需要获取headers
        $headers = $this->request->header();

        if (!defined('IS_ACCESS_TOKEN_CHECKED'))
        {
            if (isset($headers['app-type']))
            {
                if (strpos(config('zyhsapp.app_types'), $headers['app-type']) !== false)
                {
                    if (isset($headers['app-type']) && in_array($headers['app-type'], ['flutter-web', 'flutter-mac', 'flutter-win', 'flutter-linux'], true))
                    {
                        if (md5($headers['app-type'] . $headers['time'] . $headers['version'] . 'b0Rrpe5cuSONdDEz') !== $headers['sign'])
                        {
                            Log::write('flutter web 验签错误：app-type=' . $headers['app-type'] . ',time=' . $headers['time'] . ',version=' . $headers['version'] . ',sign=' . $headers['sign'], 'error', true);
                            throw new ApiException('flutter web sign验签失败');
                        }
                    } else
                    {
                        // APP
                        if (!isset($headers['sign']))
                        {
                            Log::write('请求缺少sign', 'error', true);
                            throw new ApiException('接口请求失败');
                        }
                        // 需要sign
                        if (!IAuth::checkSignPass($headers, 'zyhsapp'))
                        {
                            Log::write('sign验签失败', 'error', true);
                            throw new ApiException('接口请求失败');
                        }
                    }

                } else
                {
                    Log::write('当前设备不支持', 'error', true);
                    throw new ApiException('当前设备不支持');
                }
            } else
            {
                Log::write('当前设备不支持', 'error', true);
                throw new ApiException('当前设备不支持');
            }
            // 1、文件  2、mysql 3、redis   由于服务器为集群架构   此处使用redis
            define('IS_ACCESS_TOKEN_CHECKED', true);
        }
        // 将当前请求头信息存入header属性中，以便后面使用
        $this->headers = $headers;
    }

    /**
     * 检测手机号格式，不可注册
     * @param $tel
     * @param int $areaCode 手机区号
     * @return array
     * @throws ApiException
     */
    protected function CheckUserTel($tel, $areaCode = 86)
    {
        // 手机号是否存在
        if (!Util::telRuleCheck($tel, $areaCode))
        {
            Log::write($areaCode . $tel . '手机号错误', 'error');

            throw new ApiException('手机号错误', 200, 0);
        }
        //判断手机号是否注册过
        try
        {
            $tel = $areaCode === 86 ? $tel : $areaCode . '-' . $tel;

            $user = (new ZyhsUser())
                ->field('id,forbidden,has_self_recode,password,young_pwd,nickname,uc_id,avatar,token_expire,token,uen,is_login_record')
                ->where(['tel' => $tel])->findOrFail()->toArray();
        }
        catch (\Exception $e)
        {
            $user = [];
        }
        return $user;
    }
}
